<template>
  <div>
    <a-modal
      :title="currentEditType.type === 'create' ? '新增算子' : '编辑算子'"
      :visible="visible"
      width="60%"
      centered
      ok-text="保存"
      :destroy-on-close="true"
      cancel-text="取消"
      @ok="handleOk"
      @cancel="handleCancel"
    >
      <div class="add-edit-operator">
        <div class="setting-code">
          <!-- 左侧 -->
          <div class="left">
            <!-- 分组 算子名称 -->
            <div class="location">
              <a-input
                v-model="operatorName"
                class="name"
                placeholder="算子名称"
              />
            </div>
            <!--            <div class="location">-->
            <!--              <a-input v-model="functionName" class="name" placeholder="函数名称" />-->
            <!--            </div>-->
            <!-- 帮助链接 -->
            <div class="help">
              <a-textarea
                v-model="description"
                class="link"
                placeholder="算子描述..."
                :rows="4"
              />
            </div>
            <!-- 参数配置 -->
            <div class="header">
              <div class="label">参数配置</div>
              <a-icon
                class="add-btn"
                type="plus"
                @click="addOperatorParameters"
              />
            </div>
            <operator-parameter-tree
              class="parameters"
              :params="operatorParameters"
              @add-option="addOption"
              @delete-option="deleteOption"
              @delete="deleteParameter($event)"
            />
            <!-- 输出接口配置  -->
            <div class="header">
              <div class="label">输出接口配置</div>
              <a-icon class="add-btn" type="plus" @click="addOperatorOutput" />
            </div>
            <operator-output-tree
              class="parameters"
              :interfaces="outputInterfaces"
              @add-column="addColumn"
              @delete-column="deleteColumn"
              @delete="deleteInterface($event)"
            />
          </div>
          <!-- 右侧  -->
          <div class="right">
            <div class="header">
              <div class="label">源代码</div>
              <div class="code-type-sdk">
                <div class="edit-operator-tips">
                  <a-tooltip
                    placement="bottom"
                    :overlay-style="{ width: '250px' }"
                  >
                    <template slot="title">
                      <span>
                        1.
                        脚本需定义入口函数begin_alg，且必须返回如模板中定义的json结构体;</span
                      >
                      <br />
                      <span>
                        2. begin_alg参数顺序和左侧"参数配置"保持一致；</span
                      >
                      <br />
                      <span>
                        3. 源数据表参数以"source_table"开头,
                        输出表参数以"out_table"开头;</span
                      >
                      <br />
                      <span>
                        4.
                        存在输出表参数时需要在"输出接口配置"中定义输出表meta信息，且要一一对应;</span
                      >
                    </template>
                    <a-icon-font type="iconbangzhu" />
                  </a-tooltip>
                </div>
                <a-select v-model="languageType" style="width: 120px;">
                  <a-select-option value="python3">python3</a-select-option>
                  <a-select-option value="Spark" disabled
                    >Spark(暂不支持)</a-select-option
                  >
                </a-select>
                <div class="sdk-btn">
                  <a :href="sdk" target="_blank">SDK文档</a>
                </div>
                <div class="sdk-btn" @click="debugRun">
                  调试
                </div>
              </div>
            </div>
            <!-- 编辑框 -->
            <div class="source-editor">
              <textarea
                ref="container"
                v-model="scriptBody"
                class="monaco-editor"
              ></textarea>
            </div>
          </div>
        </div>
        <!--   调试     -->
        <div class="operator-debug" :class="{ 'debug-success': debugStatus }">
          {{ debugLog }}
        </div>
      </div>
    </a-modal>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
import { Prop, Watch } from 'vue-property-decorator'
import OperatorOutputTree from '@/components/studio/data/edit-operator/OperatorOutputTree.vue'
import OperatorParameterTree from '@/components/studio/data/edit-operator/OperatorParameterTree.vue'
import 'codemirror/lib/codemirror.css'
import 'codemirror/theme/blackboard.css'
import * as CodeMirror from 'codemirror'
import 'codemirror/addon/edit/matchbrackets'
import 'codemirror/addon/selection/active-line'
import 'codemirror/mode/python/python'
import {
  saveOperator,
  updateOperator,
  debugOperator,
  queryNotebook,
} from '@/api/edit-operator'
import { dealDataForOperator } from '@/components/studio/data/util'

@Component({
  components: {
    OperatorOutputTree,
    OperatorParameterTree,
  },
})
export default class EditOperator extends Vue {
  @Prop() visible!: boolean
  @Prop() currentEditType!: any
  // 算子名称
  public operatorName: string = ''
  // 函数名称
  public functionName: string = ''
  // 帮助连接
  public description: string = ''
  // 语言
  public languageType: string = 'Python3'
  // 编辑框
  public pythonCodeMirror: any = {}
  // code
  public scriptBody = ''
  // sdk
  public sdk = ''
  // 当前算子id  （新增的时候保存一下，再debug）
  public currentId: number | null = null

  // 参数配置
  public operatorParameters: any[] = []
  public inputScriptHead: string = ''
  // 输出接口
  public outputInterfaces: any[] = []
  public outputScriptTail: string = ''

  // debug log
  public debugLog: string = ''
  public debugStatus: boolean = true

  // 监听弹出 渲染编辑器
  @Watch('visible')
  private _onOpenEditOperator(visible: boolean) {
    this.currentId = null
    if (visible) {
      this.addEditOperator()
    }
  }

  /**
   * 模板/原算子 创建编辑器
   */
  public createCodeMirror() {
    const that = this
    setTimeout(() => {
      this.pythonCodeMirror = CodeMirror.fromTextArea(
        this.$refs.container as HTMLTextAreaElement,
        {
          mode: 'text/x-python',
          indentWithTabs: true,
          smartIndent: true,
          lineNumbers: true,
          lineWrapping: false,
          matchBrackets: true,
          theme: 'blackboard',
          autofocus: true,
          tabSize: 4,
          indentUnit: 4,
          extraKeys: {
            // enter 先换行 再添加n个空格
            Enter: (cm: any) => {
              const lineCode = cm.getLine(cm.getCursor().line)
              let count: number = lineCode.indexOf(
                lineCode.replace(/^\s*/, '').slice(0, 1)
              )
              if (count === 0) {
                count = 4
              }
              let repeatString: string = ''
              for (let i = 0; i < count; i += 1) {
                repeatString += ' '
              }
              cm.replaceSelection('\n')
              cm.replaceSelection(repeatString)
            },
            Tab: (cm: any) => {
              if (cm.somethingSelected()) {
                // 存在文本选择
                // cm.indentSelection('add')    // 正向缩进文本
                // 使用空格缩进
                cm.replaceSelection(
                  new Array(cm.getOption('indentUnit') + 1).join(' ') +
                    cm.getSelection()
                )
              } else {
                // 无文本选择
                cm.replaceSelection(
                  new Array(cm.getOption('indentUnit') + 1).join(' ')
                )
              }
            },
            // 'Shift-Tab': (cm: any) => {              // 反向缩进
            //   if (cm.somethingSelected()) {
            //     cm.indentSelection('subtract')
            //   } else {
            //     cm.indentSelection('subtract')
            //   }
            // },
          },
        }
      )
      this.pythonCodeMirror.on('cursorActivity', () => {
        that.scriptBody = that.pythonCodeMirror.getValue()
      })
    }, 100)
  }

  /**
   * 新增编辑算子
   */
  public async addEditOperator() {
    // 清空debuglog
    this.debugLog = ''
    let data = {}
    if (this.currentEditType.type === 'edit') {
      data = { id: this.currentEditType.id }
    }
    const response = await queryNotebook({ data })
    if (response.data.code === 100) {
      // 算子名称
      this.operatorName = response.data.result.name
      // 函数名
      this.functionName = response.data.result.functionName
      // 算子描述
      this.description = response.data.result.description
      this.scriptBody = response.data.result.scriptBody
      // sdk
      this.sdk = response.data.result.sdk
      // 语言
      this.languageType = response.data.result.languageType
      // 配置参数
      this.operatorParameters = response.data.result.inputParamsObj.input
      this.inputScriptHead = response.data.result.inputParamsObj.script_head
      // 处理
      this.operatorParameters.forEach((item: any, index: number) => {
        // 折叠
        this.$set(this.operatorParameters[index], 'collapsed', false)
        // items 由 []  转换 [{k:v}]
        if (
          this.operatorParameters[index].items &&
          this.operatorParameters[index].items.length > 0
        ) {
          this.$set(
            this.operatorParameters[index],
            'items',
            this.operatorParameters[index].items.map(
              (option: string | number) => {
                return { value: option }
              }
            )
          )
        } else {
          this.$set(this.operatorParameters[index], 'items', [])
        }
      })
      // 输出参数
      this.outputInterfaces = response.data.result.outputParamsObj.output
      this.outputScriptTail = response.data.result.outputParamsObj.script_tail
      // 处理
      this.outputInterfaces.forEach((item: any, index: number) => {
        this.$set(this.outputInterfaces[index], 'collapsed', false)
        this.$set(this.outputInterfaces[index], 'columnCollapsed', false)
      })
      this.createCodeMirror()
    }
  }

  // 添加配置参数
  public addOperatorParameters() {
    if (this.operatorParameters[this.operatorParameters.length - 1].name) {
      this.operatorParameters.push({
        name: '',
        english_name: '',
        type: 'string',
        paramType: 'char',
        default: '',
        value: '',
        items: [],
        collapsed: false,
      })
    } else {
      this.$message.warning('请先编辑已添加的参数!')
    }
  }
  /**
   * 删除配置参数
   */
  public deleteParameter(parameter: { parameter: any; index: number }) {
    this.operatorParameters.splice(parameter.index, 1)
  }

  /**
   * 给参数中 items 添加 option (select checkbox)
   * @param index 参数索引
   */
  public addOption(index: number) {
    this.operatorParameters[index].items.push({ value: '' })
  }
  /**
   * 删除 items 中 option (select checkbox)
   * @param parameter 参数索引 和 option 索引
   */
  public deleteOption(parameter: { index: number; i: number }) {
    this.operatorParameters[parameter.index].items.splice(parameter.i, 1)
  }

  /**
   * 添加输出
   */
  public addOperatorOutput() {
    if (this.outputInterfaces[this.outputInterfaces.length - 1].tableName) {
      this.outputInterfaces.push({
        tableName: '',
        meta: [
          {
            name: '字段1',
            type: 'char',
          },
        ],
        collapsed: false,
        columnCollapsed: false,
      })
    } else {
      this.$message.warning('请先编辑已添加的输出!')
    }
  }

  /**
   * 删除输出
   */
  public deleteInterface(output: { parameter: any; index: number }) {
    this.outputInterfaces.splice(output.index, 1)
  }

  /**
   * 添加输出列
   */
  public addColumn(index: number) {
    this.outputInterfaces[index].meta.push({
      name: '',
      type: 'char',
    })
  }

  /**
   * 删除输出列
   */
  public deleteColumn(output: { index: number; i: number }) {
    this.outputInterfaces[output.index].meta.splice(output.i, 1)
  }

  /**
   *  debug
   */
  public debugRun() {
    // 新建的算子调试
    let id: number | null
    if (this.currentEditType.type === 'create' && !this.currentId) {
      this.$message.warning('请先保存后调试！')
      return
    }
    // 算子id
    if (this.currentEditType.type === 'create') {
      id = this.currentId
    } else {
      id = this.currentEditType.id
    }
    this.$message.warning('请先确认保存后进行调试, 以保证测试结果！')
    const that = this
    debugOperator({ data: { id } }).then((response) => {
      if (response.data.code === 100) {
        if (response.data.result.status === 0) {
          setTimeout(() => {
            that.$message.success('自定义算子调试成功!')
          }, 1000)
          this.debugStatus = true
        } else {
          setTimeout(() => {
            that.$message.error('自定义算子调试失败!')
          }, 1000)
          this.debugStatus = false
        }
        this.debugLog = response.data.result.output
      }
    })
  }
  /**
   * 确认 添加  / 编辑
   */
  public handleOk() {
    const data = dealDataForOperator({
      operatorParameters: this.operatorParameters,
      outputInterfaces: this.outputInterfaces,
    })
    data.inputParamsObj.script_head = this.inputScriptHead
    data.outputParamsObj.script_tail = this.outputScriptTail
    data.scriptBody = this.scriptBody
    data.name = this.operatorName
    data.functionName = this.functionName
    data.description = this.description
    data.sdk = this.sdk
    data.languageType = this.languageType
    if (this.currentEditType.type === 'edit') {
      data.id = this.currentEditType.id
      this.updateOperator(data)
    } else {
      if (this.currentId) {
        data.id = this.currentId
        this.updateOperator(data)
        return
      }
      this.saveOperator(data)
    }
  }

  /**
   * 新增算子
   * @param data
   */
  public async saveOperator(data: any) {
    const response = await saveOperator({ data })
    if (response.data.code === 100) {
      this.$message.success(response.data.message)
      this.currentId = response.data.result
      this.$emit('add-success')
    }
  }

  /**
   * 更新算子
   */
  public async updateOperator(data: any) {
    const response = await updateOperator({ data })
    if (response.data.code === 100) {
      this.$message.success(response.data.message)
      this.$emit('add-success')
    }
  }

  /**
   * 取消编辑
   */
  public handleCancel() {
    this.closePop()
  }

  /**
   * 关闭弹框
   */
  public closePop() {
    this.$emit('close-pop')
  }
}
</script>

<style lang="less" scoped>
.add-edit-operator {
  background-color: #fff;
  display: flex;
  flex-direction: column;
  height: 70vh;
  /* 配置和代码 */

  .setting-code {
    display: flex;
    height: 80%;

    /* 左侧 */
    .left {
      display: flex;
      flex: 0 0 30%;
      flex-direction: column;

      /* 算子名称 函数名 */

      .location {
        display: flex;
        flex: 0 0 30px;
        flex-direction: row;
        height: 30px;
        margin-bottom: 10px;

        .name {
          border: 1px solid #ddd;
          display: block;
          flex: 1 1;
          height: 100%;
          outline: none;
          padding: 0 10px;
          width: 100%;
        }
      }

      /* 帮助和简介 */

      .help {
        flex: 0 0 30px;
        height: 50px;

        .link {
          border: 1px solid #ddd;
          box-sizing: border-box;
          display: block;
          height: 100%;
          outline: none;
          padding: 0 10px;
          width: 100%;
        }
      }

      .header {
        align-items: center;
        display: flex;
        font-size: 14px;
        justify-content: space-between;
        margin-top: 20px;
        position: relative;

        .add-btn {
          cursor: pointer;
          position: absolute;
          right: 0;
          top: 50%;
          transform: translateY(-50%);

          &:hover {
            color: #6570ff;
          }
        }
      }

      .parameters {
        flex: 1 1;
        min-height: 0;
        overflow-y: auto;
        padding-right: 10px;
      }
    }
    /* 右侧 */
    .right {
      display: flex;
      flex-direction: column;
      font-size: 0.9rem;
      margin-left: 20px;
      position: relative;
      width: calc(70% - 20px);

      .header {
        display: flex;
        height: 32px;
        justify-content: space-between;
        line-height: 32px;

        .code-type-sdk {
          display: flex;
          justify-content: flex-end;

          .edit-operator-tips {
            color: #e74c3c;
            cursor: pointer;
            font-size: 20px;
            padding: 0 12px;
          }

          .sdk-btn {
            border: 1px solid #1e49f8;
            border-radius: 4px;
            color: #1e49f8;
            cursor: pointer;
            height: 32px;
            margin-left: 12px;
            text-align: center;
            width: 100px;
          }
        }
      }

      /* 代码 */

      .source-editor {
        border: 1px solid #888;
        flex: 1 1;
        margin-top: 10px;
        min-height: 0;
        width: 100%;

        .monaco-editor {
          border: 1px solid #ebebeb;
          border-radius: 4px;
          height: 100%;
          width: 100%;
        }

        .code {
          border: 1px solid #ddd;
          box-sizing: border-box;
          line-height: 24px;
          min-height: 100%;
          outline: none;
          padding: 5px 10px;
          width: 100%;
        }
      }

      /deep/ .cm-s-default {
        height: 100% !important;
      }

      /deep/ .cm-s-blackboard {
        height: 100% !important;
        width: 100%;
      }
    }
  }
  /* debug */
  .operator-debug {
    border: 1px solid #ddd;
    border-radius: 3px;
    color: #e74c3c;
    height: calc(20% - 12px);
    margin-top: 12px;
    overflow-y: auto;
    padding: 12px;

    &::-webkit-scrollbar {
      height: 10px;
      width: 6px;
    }

    &::-webkit-scrollbar-track {
      background: rgb(239, 239, 239);
      border-radius: 2px;
    }

    &::-webkit-scrollbar-thumb {
      background: #bfbfbf;
      border-radius: 6px;
    }

    &::-webkit-scrollbar-thumb:hover {
      background: #333;
    }

    &::-webkit-scrollbar-corner {
      background: #179a16;
    }
  }

  .debug-success {
    color: #179a16;
  }
}
</style>
